第二十三課:SearchItem搜尋欄filter真實資料的實作 query得各種混合應用
前幾天我們都了解並完成了context的部分,現在要開始進到後面的大重點,將我們的searchBar啟用,可以用它來查詢我們的飯店資料,這個熟悉後,後台的所有資料爬梳都可以應用在這個概念上。
並今天我們要來完成我們的SearchItem搜尋欄,讓他條件搜尋是可以串接到我們的後端資料庫抓出我們想要的資料。
我們之前學到Api中的我們可以利用url的改變來查詢我們想要的資料,包括了city,type到是不是popularHotels熱門排行。
為了完成我們的SearchItem搜尋欄,我們要同時具備三種filter功能,城市、最低價格與最高價格,這邊特別注意入住時間與人數算是我們的使用者行為條件,所以先不做filter部分,而是我們會希望入住時間與人數到時候是到更詳細的hotelPage去做比對看有沒有能夠容納這些人數與有空房的時間。
首先先來完成城市的搜尋
所以一樣先利用到的是useFetch,就跟我們用在首頁feature的情況一樣
完整的url應該可以透過下面來抓到我們在nodeApi寫好的getHotels函數
http://localhost:5000/api/v1/hotels?city=高雄
並我們其實希望當他沒有找到任何資料時就主動回傳推薦名單,所以我們可以來更改我們的url,也就是當搜尋欄的city為""空值時,回傳我們的popularHotels列表,展示在我們的hotelsList內。所以我們會用到?:條件句,並因為我們常常要在更改我們的url,所以我拉出來特別宣告,
const city ="台北" //這邊到時候city都會換成useState的destination
const searchUrl = `/hotels?${city ? "city=" + city : "popularHotel=true" }`
const {data,loading,error} =useFetch(searchUrl)
// console.log(data)
並把資料套到SearchItem上並map出來
{loading ? <>搜尋載入中</> :
data.map((item,index) =>
<SearchItem active={index==0 && "active"}
key={item._id} dataDetail={item} conditions={conditions} dates={dates} />
)
}
我們之前在列SearchItem UI時,打了很多測試資訊上去,現在要把這些都替換成真實資料,所以我們要先來分析一下,這些資料的類型與傳入方式。
並這邊我們會先處理導入資料進searchItem框架,而"五晚、一位"等資料,想要等我們弄完hotelPage串接時一次把這些價格跟住了幾晚的條件資料一次搞定,現在都先來把原本的資料庫的資料串接上我們的UI。所以回到我們的searchItem.jsx Component來接這些props,
好了後應該會長
並要來把測試city變數換掉完成真實的useState的destination的input結果
這邊會有一點小複雜,代為的就是能讓我們的fetchData可以在每次按下按鈕就重新搜尋,重新跑一次useFetch,所以用到的是url的變動來帶動整個useFetch中useEffect讓useFetch再跑一次,而每次url的變動與重新宣吿,都希望是跟著按按鈕一起,所以寫在handleClick內。
const searchUrl = `/hotels?${destination ? "city=" + destination : "popularHotel=true" }`
const [fetchDataUrl,setFetchDataUrl]=useState(searchUrl)//useFetch要能夠重新搜尋就改動url讓他可以重整
const {data,loading,error} =useFetch(fetchDataUrl)
const handleClick = () => {
dispatch({ type: new_Options, payload: { city: destination, date: dates, options: conditions } })
setFetchDataUrl(searchUrl)
}
然後useFetch的useEffect也要加上他的dependency
const useFetch = (url) => {
const [data, setData]=useState([]);
const [loading,setLoading]=useState(false);
const [error, setError] =useState("");
useEffect(()=>{
const fetchData =async()=>{
setLoading(true);
try{
const response = await axios.get(url)
setData(response.data)
}catch(err){
setError(err)
}
setLoading(false);
}
fetchData()
},[url]) //這邊有改動加上url
return {data,loading, error}
}
完成後應該搜尋就會有結果
並在加一進去一些資料連動,然後使用optionsContext的操作資料更新,這邊如果導入得是destinations就會跟我們searchBar一樣快速連動,但我們希望他是跟著搜尋後,在更新title與資料一起顯示,所以我們可以利用的我們context的dispatch,並抓更新過後的context裡面的state,所以就是不想要那麼快連動可以在拉出來多一層的概念。
這邊大致上完成後,來應用day20時那時候教的loading載入畫面,並一樣會用到我們的fetchData中的loading。
所以首先我們一樣先處理我們的skeleton loading export是要符合這邊的Searchitem的遮罩樣子。
這邊就不多花太多時間在這邊,附上相關連結
skeleton loading.div github連結
skeleton loading.scss github連結
完成了簡單的地點filter功能後,我們要來加價格搜尋的功能。
這邊我們既然要增加能夠搜尋最高價格與最低價格的filter功能,首先Api部分我們就要做更改,所以要回到我們Api folder內
//getAllHotels升級版2.0,讓他能抓取全部資料包括新的價格區間也能依照query值去找想要的資料
export const getAllHotels = async(req,res,next)=>{
const {lowestPrice,highestPrice,...withQuery} = req.query;
//如果url上有寫popularHotels=true,popularHotels會回傳true 沒有寫就沒這條件
try{
const hotelsList = await Hotel.find(
{
...withQuery,
cheapestPrice:{$gt:lowestPrice || 0,
$lt:highestPrice || 9999} //這邊一定要這樣打因為涉及到兩個fetch 如果沒有填|| 會出現沒有值的問題
}
).limit(7) //讓他回傳資料最多就七個
res.status(200).json(hotelsList)
}catch(error){
next(errorMessage(500,"無法抓取所有飯店資料",error))
}
}
更改好我們的Api後,這邊要確定能不能運行通常也會去insomnia去檢測,但這邊我們確定ok就一樣先回到我們的client side
const [lowestPrice, setLowestPrice] = useState("");
const [highestPrice, setHighestPrice] = useState("");
const searchUrl =
`/hotels?${destination ? "city=" + destination : "popularHotel=true"} &lowestPrice=${lowestPrice} &highestPrice=${highestPrice}`
<div className="listItemLimitPrice">
<span className="limitTitle">
每晚最低價格
</span>
<input type="text" className='searchInput' onChange={(e)=>setLowestPrice(e.target.value)}/>
</div>
<div className="listItemLimitPrice">
<span className="limitTitle">
每晚最高價格
</span>
<input type="text" className='searchInput' onChange={(e)=>setHighestPrice(e.target.value)}/>
</div>
最後這樣子就完成了我們的價格區間搜尋機制,
類似的資料爬梳除了要前後端合作去產出外,有時候也要考慮說是前端這處理比較多還是在Api部分處理比較多,這些都會影響到資料回傳的速度,舉例來說,如後台架設之時,常常會遇到很多需要資料爬梳的情況,常見的月營收、平均顧客等等的,後端能現成的Api資料就可以直接抓來使用,但更多是像我們前後端都自身串連,可能就會需要在前端上做函數計算,鐵人賽轉眼間也越來越接近完賽,大概的心得也就是結果不重要,但過程中自身學到了很多,累卻也有成就感,希望能完美的落幕。